package com.apobates.forum.bucket.thumbnail.handler;

import com.apobates.forum.utils.image.OverlayBox;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.Thumbnails.Builder;
/**
 * 从中心点开始裁剪.支持缩放
 *
 * @author xiaofanku
 * @since 20200529
 */
public final class ZoomFixedCropHandler extends AbstractCropImageHandler {
    //图片的缩放比例(1-100)
    private final int scale;
    
    /**
     * 初始化
     *
     * @param orginalImageFile 原始图片文件
     * @param scale 缩放比例
     */
    public ZoomFixedCropHandler(File orginalImageFile, int scale) {
        super(orginalImageFile);
        this.scale = scale;
    }
    
    /**
     * 初始化
     *
     * @param orginalImageFile 原始图片文件
     * @param waterBox 遮盖盒
     * @param scale 缩放比例
     */
    public ZoomFixedCropHandler(File orginalImageFile, OverlayBox waterBox, int scale) {
        super(orginalImageFile, waterBox);
        this.scale = scale;
    }
    
    @Override
    public void cropImage(File cropSaveFile, int width, int height) throws IOException {
        BufferedImage originalImage = ImageIO.read(getOrginalImageFile());
        int aw = originalImage.getWidth(); //A的宽
        int ah = originalImage.getHeight(); //A的高
        String fileExt = getFileExtension(getFileName(cropSaveFile));
        //水印是否有设置
        BufferedImage watermarkOverlay = null;
        try {
            watermarkOverlay = getWatermark();
        } catch (IOException | NullPointerException e) {
        }
        if (width == 0 && height == 0) { //自动
            Builder<File> b = Thumbnails.of(getOrginalImageFile()).scale(Math.abs(scale) / 100.00D);
            if (null != watermarkOverlay) { //若有设置增加水印设置
                b = b.watermark(getThumbnailPosition(), watermarkOverlay, getWaterBoxOpacity());
            }
            b.outputFormat(fileExt).toFile(cropSaveFile);
            return;
        }
        if (aw == width && ah == height) { //无需裁剪
            saveFile(originalImage, watermarkOverlay, fileExt, cropSaveFile);
            return;
        }
        //计算缩放的比例
        double scaleRate = calcScaleRate(aw, ah, width, height);
        //缩放
        BufferedImage data = scaleQuality(originalImage, aw, ah, scaleRate);
        //中心法裁剪
        int[] xyPointers = getCropXYPointer(data.getWidth(), data.getHeight(), width, height);
        if (xyPointers.length != 2) {
            throw new IOException("起始坐标计算失败");
        }
        //最后一公里
        try (ByteArrayOutputStream os = new ByteArrayOutputStream(); ImageOutputStream ios = ImageIO.createImageOutputStream(os)) { //20200329@AutoCloseable
            BufferedImage croppedImage = data.getSubimage(xyPointers[0], xyPointers[1], width, height);//
            //
            ImageWriter writer = ImageIO.getImageWritersByFormatName(fileExt).next();
            //
            ImageWriteParam param = writer.getDefaultWriteParam();
            if (fileExt.equalsIgnoreCase("bmp")) {
                try {
                    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                    param.setCompressionType("BI_RGB");
                } catch (java.lang.UnsupportedOperationException e) {
                }
            } else if (fileExt.equalsIgnoreCase("gif")) {
                try {
                    param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
                } catch (java.lang.UnsupportedOperationException e) {
                }
            } else {
                try {
                    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                    param.setCompressionQuality(1.0f);
                } catch (java.lang.UnsupportedOperationException e) {
                }
            }
            //
            writer.setOutput(ios);
            writer.write(null, new IIOImage(croppedImage, null, null), param);
            writer.dispose();
            //
            ByteArrayInputStream in = new ByteArrayInputStream(os.toByteArray()); //将b作为输入流；
            BufferedImage image = ImageIO.read(in);
            saveFile(image, watermarkOverlay, fileExt, cropSaveFile);
        }
    }
    
    //没有设置水印直接保存
    //若有设置,改用OverlayBox.toFile(BufferedImage, File)保存
    private void saveFile(BufferedImage im, BufferedImage watermarkOverlay, String formatName, File output) throws IOException {
        //若有设置增加水印设置 && 水印与图片的面积是否允许水印
        if (null != watermarkOverlay) {
            getWaterBox().watermark(im, watermarkOverlay, output);
            return;
        }
        ImageIO.write(im, formatName, output);
    }
    
    //计算缩放的比例
    //以宽度为参考,让高度满足最小大于
    private double calcScaleRate(int originalImageWidth, int originalImageHeight, int cropWidth, int cropHeight) {
        double scaleRate = cropWidth / (originalImageWidth * 1.00D);
        //当前是缩小?
        boolean isZoomOut = originalImageWidth > cropWidth;
        
        int currentHeight = Double.valueOf(originalImageHeight * scaleRate).intValue();
        while (currentHeight < cropHeight) {
            //提升多少呢?
            if (isZoomOut) {
                scaleRate += 0.1D;
            } else {
                scaleRate += 1;
            }
            currentHeight = Double.valueOf(originalImageHeight * scaleRate).intValue();
        }
        return scaleRate;
    }
    
    //缩放图像
    private BufferedImage scaleQuality(BufferedImage originalImage, int originalImageWidth, int originalImageHeight, double scaleRate) {
        int w, h;
        w = Double.valueOf(originalImageWidth * scaleRate).intValue();
        h = Double.valueOf(originalImageHeight * scaleRate).intValue();
        //构建图片对象
        BufferedImage _image = new BufferedImage(w, h, originalImage.getType());
        //画质优先
        AffineTransform transform = AffineTransform.getScaleInstance(scaleRate, scaleRate);
        AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BICUBIC);
        op.filter(originalImage, _image);
        return _image;
    }
    
    /**
     * 获取裁剪的起始点坐标
     *
     * @param originalImageWidth 图片的宽度
     * @param originalImageHeight 图片的高度
     * @param width 裁剪的宽度
     * @param height 裁剪的高度
     * @return
     */
    private int[] getCropXYPointer(int imageWidth, int imageHeight, int width, int height) {
        if (imageWidth <= width && imageHeight <= height) {
            return new int[]{};
        }
        int startX = 0;
        if (imageWidth > width) {
            int centerX = imageWidth / 2; //宽度中心点X
            startX = centerX - width / 2;
        }
        //条漫?
        if (isVerticalComic(imageWidth, imageHeight)) {
            return new int[]{startX, 0};
        }
        int startY = 0;
        if (imageHeight > height) {
            int centerY = imageHeight / 2; //高度中心点Y
            startY = centerY - height / 2;
        }
        return new int[]{startX, startY};
    }
    
    private boolean isVerticalComic(int imageWidth, int imageHeight) {
        return imageHeight / imageWidth >= 2;
    }
}